package com.sunnyweather.android.logic

import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.liveData
import com.sunnyweather.android.logic.dao.PlaceDao
import com.sunnyweather.android.logic.dao.WeatherDao
import com.sunnyweather.android.logic.model.*
import com.sunnyweather.android.logic.network.SunnyWeatherNetWork
import com.sunnyweather.android.logic.service.AMapPositionService
import com.sunnyweather.android.logic.util.LogUtil
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext

object Repository {

    //在仓库层定义获取数据的函数，而这些函数具体使用的是本地数据还是云端数据则取决于仓库层函数的逻辑实现


    fun queryPlaces(query:String)= fire(Dispatchers.IO){
        //LogUtil.d("Repository.queryPlaces:","$query")
        val placeResponse=SunnyWeatherNetWork.queryplaces(query)
        if(placeResponse.status=="ok"){
            Result.success(placeResponse.places)
        }else{
            //查询失败，返回空数组
            Result.success(listOf())
        }
    }

    //从网络获取天气信息
    fun refreshWeather(lat:Double,lng:Double)= fire(Dispatchers.IO){
        coroutineScope {
            //实时天气与未来几天天气预报实时查询
            val realtimeDeferred=async {
                SunnyWeatherNetWork.getRealTimeWeather(lat, lng)
            }
            val dailyDeferred=async {
                SunnyWeatherNetWork.getDailyWeather(lat, lng)
            }
            LogUtil.d("Repository.refreshWeather",realtimeDeferred.toString()+"////"+dailyDeferred.toString())
            var realTimeResponse=RealTimeResponse(null,null)
            var dailyResponse=DailyResponse(null,null)
            //设置协程超时处理,如果出现错误将会转到fire函数的catch处
            withTimeoutOrNull(8000){
                  realTimeResponse=realtimeDeferred.await()
                  dailyResponse=dailyDeferred.await()
            }
            LogUtil.d("Repository.refreshWeather.await",realTimeResponse.toString()+"////"+dailyResponse.toString())
            if(realTimeResponse.status=="ok"&&dailyResponse.status=="ok"){
                val weather=Weather(realTimeResponse.result?.realtime,dailyResponse.result?.daily)
                WeatherDao.addWeather(Location(lat,lng).toString(),weather)
                Result.success(weather)
            }else{
                LogUtil.d("Repository.refreshWeather.failure",realTimeResponse.toString()+"////"+dailyResponse.toString())
                Result.failure(RuntimeException("Realtime status is ${realTimeResponse.status},Daily status is ${dailyResponse.status}"))
            }
        }
    }

    //从本地缓存获取天气数据
    fun getWeather(key: String)= WeatherDao.getWeather(key)

    //从本地缓存移除指定地区的天气信息
    fun removeWeather(lat:Double,lng:Double)=WeatherDao.removeWeather(Location(lat, lng).toString())

    //判断本地是否存在某个地点的天气信息
    fun isWeatherExist(key: String)=WeatherDao.isExist(key)
    //向缓存中新增天气
    fun addWeather(key: String,weather: Weather)=WeatherDao.addWeather(key,weather)

    //存储地点
    fun setPlace(place: Place)= PlaceDao.setPlace(place)

    //获取地点
    fun getRestoredPlace()= PlaceDao.getRestoredPlace()

    fun isStored()=PlaceDao.isStored()


    //对地点列表的操作
    fun addPlace(place: Place)=PlaceDao.addPlace(place)

    fun removePlace(key:String)=PlaceDao.removePlace(key)

    fun getPlaceFromList(key:String)=PlaceDao.getPlaceFromList(key)

    fun getPlaceIndex()=PlaceDao.index

    fun removeAllPlaces()=PlaceDao.removeAllPlaces()


    //后台刷新所有已存储地点的天气
    //之前出现bug的问题在于，如果直接调用refreshWeather函数来进行网络请求，
    // 虽然refreshWeather函数内部有async函数，但是它只会阻塞refreshWeather
    // 函数所在的线程，而不会阻塞updateAllWeather函数所在的线程，而无论是
    //打印日志还是判断结果，代码都是写在refreshWeather外的，所以自然一直没有值。
    suspend fun updateAllWeather():Boolean{
        val localPlace= getRestoredPlace()
        val placeList= mutableListOf<Place>()
        var flag=0
        placeList.add(localPlace)
        for(i in 0 until getPlaceIndex()){
            placeList.add(getPlaceFromList(i.toString()))
        }
        for(i in 0 until placeList.size){
            val place=placeList[i]
            val job=Job()
            val scope= CoroutineScope(job)
            //每循环一次都开启一个子线程去发起一个请求
            var result=scope.async {
                LogUtil.d("Repository.updateAllWeather",Thread.currentThread().name)
                val realtimeDeferred=async {
                    SunnyWeatherNetWork.getRealTimeWeather(place.location.lat,place.location.lng)
                }
                val dailyDeferred=async {
                    SunnyWeatherNetWork.getDailyWeather(place.location.lat,place.location.lng)
                }
                val realTimeResponse=realtimeDeferred.await()
                val dailyResponse=dailyDeferred.await()
                var result:Result<Weather>?=null
                if(realTimeResponse.status=="ok"&&dailyResponse.status=="ok"){
                    val weather=Weather(realTimeResponse.result?.realtime,dailyResponse.result?.daily)
                    addWeather(place.location.toString(),weather)
                    result=Result.success(weather)
                }else{
                    result=Result.failure(RuntimeException("请求失败"))
                }
                result
            }.await()
            LogUtil.d("Repository.updateAllWeather.request","${result?.getOrNull().toString()}")
            if(result?.isFailure?:true){
                flag=1
            }
        }
        LogUtil.d("Repository.updateAllWeather","${flag==0}")
        return flag==0
    }




    //统一的错误处理，第一个参数是线程参数
    private fun <T> fire(context:CoroutineContext,block:suspend ()->Result<T>)= liveData(context) {
        LogUtil.d("RefreshWeather","startRefresh")
        val result=try{
            block()
        }catch(e:Exception){
            LogUtil.e("Repository.fire",e.message.toString())
            Result.failure(e)
        }
        emit(result)
    }
}